#ifndef FABRICS_H
#define FABRICS_H

#include "channel.h"
#include "port.h"
#include <complex>
#include "lane.h"

#include "ip/opdef.h"

typedef struct LaneChannelStoreFabricBusPack
{
    int addr;
    std::complex<float> data;
} LaneChannelStoreFabricBusPack;

typedef struct LaneChannelLoadFabricBusPack
{
    int pid;
    int addr;
    std::complex<float> data;
    int sb_addr;
    int ain_addr;
    bool clr;
} LaneChannelLoadFabricBusPack;

typedef struct LaneChannelLoadReqFabricBusPack
{
    int mem_addr;
    int pid;
    int sm_addr;
    int sb_addr;
    int ain_addr;
    bool clr;
} LaneChannelLoadReqFabricBusPack;

typedef struct LaneChannelExecOpFabricBusPack
{
    ExecOp nano_op;
} LaneChannelExecOpFabricBusPack;

typedef struct LaneChannelTranOpFabricBusPack
{
    TranOp nano_op;
} LaneChannelTranOpFabricBusPack;

class LaneChannelStoreFabricBusReceiver : public LanePortReceiver
{
public:
    PortIn<std::complex<float>> * data;
    PortIn<int> * addr;

    LaneChannelStoreFabricBusReceiver(JObject *parent = nullptr);
    LaneChannelStoreFabricBusPack get();
    bool is_any_empty();
    bool is_any_full();
};

class LaneChannelLoadFabricBusReceiver : public LanePortReceiver
{
public:
    PortIn<std::complex<float>> * data;
    PortIn<int> * addr;
    PortIn<int> * pid;
    PortIn<int> * sb_addr;
    PortIn<int> * ain_addr;
    PortIn<bool> * clr;

    LaneChannelLoadFabricBusReceiver(JObject *parent = nullptr);
    LaneChannelLoadFabricBusPack get();
    bool is_any_empty();
    bool is_any_full();
};

class LaneChannelLoadReqFabricBusReceiver : public LanePortReceiver
{
public:
    PortIn<int> * mem_addr;
    PortIn<int> * pid;
    PortIn<int> * sm_addr;
    PortIn<int> * sb_addr;
    PortIn<int> * ain_addr;
    PortIn<bool> * clr;

    LaneChannelLoadReqFabricBusReceiver(JObject *parent = nullptr);
    LaneChannelLoadReqFabricBusPack get();
    bool is_any_empty();
    bool is_any_full();
};

class LaneChannelExecOpFabricBusReceiver : public LanePortReceiver
{
public:
    PortIn<ExecOp> * nano_op;

    LaneChannelExecOpFabricBusReceiver(JObject *parent = nullptr);
    LaneChannelExecOpFabricBusPack get();
    bool is_any_empty();
    bool is_any_full();
};

class LaneChannelTranOpFabricBusReceiver : public LanePortReceiver
{
public:
    PortIn<TranOp> * nano_op;

    LaneChannelTranOpFabricBusReceiver(JObject *parent = nullptr);
    LaneChannelTranOpFabricBusPack get();
    bool is_any_empty();
    bool is_any_full();
};

class LaneChannelStoreFabricBusSender : public LanePortSender
{
public:
    PortOut<std::complex<float>> * data;
    PortOut<int> * addr;

    LaneChannelStoreFabricBusSender(JObject *parent = nullptr);
    void connect(LaneChannelStoreFabricBusReceiver *sender);
    void put(std::complex<float> data_data, int data_addr);
    void put_bypass(LaneChannelStoreFabricBusReceiver *receiver);
    bool is_any_full();
};

class LaneChannelLoadFabricBusSender : public LanePortSender
{
public:
    PortOut<std::complex<float>> * data;
    PortOut<int> * addr;
    PortOut<int> * pid;
    PortOut<int> * sb_addr;
    PortOut<int> * ain_addr;
    PortOut<bool> * clr;

    LaneChannelLoadFabricBusSender(JObject *parent = nullptr);
    void connect(LaneChannelLoadFabricBusReceiver *sender);
    void put(std::complex<float> data_data, int data_addr, int data_pid, int data_sb_addr, int data_ain_addr, bool data_clr);
    void put_bypass(LaneChannelLoadFabricBusReceiver *receiver);
    bool is_any_full();
};

class LaneChannelLoadReqFabricBusSender : public LanePortSender
{
public:
    PortOut<int> * mem_addr;
    PortOut<int> * pid;
    PortOut<int> * sm_addr;
    PortOut<int> * sb_addr;
    PortOut<int> * ain_addr;
    PortOut<bool> * clr;

    LaneChannelLoadReqFabricBusSender(JObject *parent = nullptr);
    void connect(LaneChannelLoadReqFabricBusReceiver *sender);
    void put(int data_mem_addr, int data_pid, int data_sm_addr, int data_sb_addr, int data_ain_addr, bool data_clr);
    void put_bypass(LaneChannelLoadReqFabricBusReceiver *receiver);
    bool is_any_full();
};

class LaneChannelExecOpFabricBusSender : public LanePortSender
{
public:
    PortOut<ExecOp> * nano_op;

    LaneChannelExecOpFabricBusSender(JObject *parent = nullptr);
    void connect(LaneChannelExecOpFabricBusReceiver *sender);
    void put(ExecOp data_nano_op);
    void put_bypass(LaneChannelExecOpFabricBusReceiver *receiver);
    bool is_any_full();
};

class LaneChannelTranOpFabricBusSender : public LanePortSender
{
public:
    PortOut<TranOp> * nano_op;

    LaneChannelTranOpFabricBusSender(JObject *parent = nullptr);
    void connect(LaneChannelTranOpFabricBusReceiver *sender);
    void put(TranOp data_nano_op);
    void put_bypass(LaneChannelTranOpFabricBusReceiver *receiver);
    bool is_any_full();
};

class ChannelStoreFabricReceiver : public ChannelPortReceiver
{
public:
    LaneChannelStoreFabricBusReceiver * Bus;

    ChannelStoreFabricReceiver(JObject *parent);
};

class ChannelLoadFabricReceiver : public ChannelPortReceiver
{
public:
    LaneChannelLoadFabricBusReceiver * Bus;

    ChannelLoadFabricReceiver(JObject *parent);
};

class ChannelLoadReqFabricReceiver : public ChannelPortReceiver
{
public:
    LaneChannelLoadReqFabricBusReceiver * Bus;

    ChannelLoadReqFabricReceiver(JObject *parent);
};

class ChannelExecOpFabricReceiver : public ChannelPortReceiver
{
public:
    LaneChannelExecOpFabricBusReceiver * Bus;

    ChannelExecOpFabricReceiver(JObject *parent);
};

class ChannelTranOpFabricReceiver : public ChannelPortReceiver
{
public:
    LaneChannelTranOpFabricBusReceiver * Bus;

    ChannelTranOpFabricReceiver(JObject *parent);
};

class ChannelStoreFabricSender : public ChannelPortSender
{
public:
    LaneChannelStoreFabricBusSender * Bus;

    ChannelStoreFabricSender(JObject *parent);
};

class ChannelLoadFabricSender : public ChannelPortSender
{
public:
    LaneChannelLoadFabricBusSender * Bus;

    ChannelLoadFabricSender(JObject *parent);
};

class ChannelLoadReqFabricSender : public ChannelPortSender
{
public:
    LaneChannelLoadReqFabricBusSender * Bus;

    ChannelLoadReqFabricSender(JObject *parent);
};

class ChannelExecOpFabricSender : public ChannelPortSender
{
public:
    LaneChannelExecOpFabricBusSender * Bus;

    ChannelExecOpFabricSender(JObject *parent);
};

class ChannelTranOpFabricSender : public ChannelPortSender
{
public:
    LaneChannelTranOpFabricBusSender * Bus;

    ChannelTranOpFabricSender(JObject *parent);
};

class ChannelStoreFabric : public Channel
{
public:
    static void connect(ChannelStoreFabricSender *sender, ChannelStoreFabricReceiver *receiver);
};

class ChannelLoadFabric : public Channel
{
public:
    static void connect(ChannelLoadFabricSender *sender, ChannelLoadFabricReceiver *receiver);
};

class ChannelLoadReqFabric : public Channel
{
public:
    static void connect(ChannelLoadReqFabricSender *sender, ChannelLoadReqFabricReceiver *receiver);
};

class ChannelExecOpFabric : public Channel
{
public:
    static void connect(ChannelExecOpFabricSender *sender, ChannelExecOpFabricReceiver *receiver);
};

class ChannelTranOpFabric : public Channel
{
public:
    static void connect(ChannelTranOpFabricSender *sender, ChannelTranOpFabricReceiver *receiver);
};

#endif // FABRICS_H